#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#define AES_BITS 128
#define MSG_LEN 128
#define MAXBUF 1024

char *key="f9476bedaa9fef0daf077f5fd33f8738";

char *testin="{\"cmd\":3,\"gtw\":\"simulator_gateway\",\"idx\":-1842138533,\"dev\":\"001\",\"data\":[{\"val\":21,\"sta\":0,\"rid\":1,\"aid\":1,\"devt\":1,\"date\":1491628764936},{\"val\":48,\"sta\":0,\"rid\":1,\"aid\":2,\"devt\":2,\"date\":1491628764936},{\"val\":12.94,\"sta\":0,\"rid\":1,\"aid\":3,\"devt\":10,\"date\":1491628764936},{\"val\":0,\"sta\":0,\"rid\":1,\"aid\":4,\"devt\":11,\"date\":1491628764936},{\"val\":89731,\"sta\":0,\"rid\":1,\"aid\":5,\"devt\":9,\"date\":1491628764936},{\"val\":237,\"sta\":0,\"rid\":1,\"aid\":6,\"devt\":7,\"date\":1491628764936},{\"val\":22,\"sta\":0,\"rid\":1,\"aid\":7,\"devt\":4,\"date\":1491628764936},{\"val\":11.24,\"sta\":0,\"rid\":1,\"aid\":8,\"devt\":5,\"date\":1491628764936}],\"ver\":0,\"date\":1491628764936}";


#define HEX_INVALID ((char)-1)

//Get the decimal value of a hexadecimal character
char GetHexValue(char ch)
{
	if (ch >= '0' && ch <= '9')
		return (ch - '0');
	if (ch >= 'A' && ch <= 'F')
		return (ch - 'A' + 10);
	if (ch >= 'a' && ch <= 'f')
		return (ch - 'a' + 10);
	return HEX_INVALID;
}

int HexDecodeGetRequiredLength(int nSrcLen)
{
	return nSrcLen/2;
}
//2个字节字符串转化为1个字节
int HexDecode(const char *pSrcData, int nSrcLen, unsigned char *pbDest, int* pnDestLen)
{
	int nRead = 0;
	int nWritten = 0;

	if (!pSrcData || !pbDest || !pnDestLen)
	{
		return 0;
	}

	if(*pnDestLen < HexDecodeGetRequiredLength(nSrcLen))
	{
		return 0;
	}

	while (nRead < nSrcLen)
	{
		char ch1,ch2;

		if((char)*pSrcData == '\r' || (char)*pSrcData == '\n')break;
		ch1 = GetHexValue((char)*pSrcData++);
		ch2 = GetHexValue((char)*pSrcData++);
		if ((ch1==HEX_INVALID) || (ch2==HEX_INVALID))
		{
			return 0;
		}
		*pbDest++ = (unsigned char)(16*ch1+ch2);
		nWritten++;
		nRead += 2;
	}

	*pnDestLen = nWritten;
	return 1;
}


#define TRUE			1
#define FALSE			0
typedef unsigned char BYTE;

int HexEncodeGetRequiredLength(int nSrcLen)
{
	return 2 * nSrcLen + 1;
}

//1个字节转换为2个字节字符串
int HexEncode(const unsigned char *pbSrcData, int nSrcLen, char *szDest, int *pnDestLen)
{
	int nRead = 0;
	int nWritten = 0;
	BYTE ch;
	static const char s_chHexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'A', 'B', 'C', 'D', 'E', 'F'};

	if (!pbSrcData || !szDest || !pnDestLen)
	{
		return FALSE;
	}

	if(*pnDestLen < HexEncodeGetRequiredLength(nSrcLen))
	{
		return FALSE;
	}

	while (nRead < nSrcLen)
	{
		ch = *pbSrcData++;
		nRead++;
		*szDest++ = s_chHexChars[(ch >> 4) & 0x0F];
		*szDest++ = s_chHexChars[ch & 0x0F];
		nWritten += 2;
	}

	*pnDestLen = nWritten;

	return TRUE;
}

void printHex(char *tag,char *buf,int len){
	char hexbuf[4096];
	int dlen=sizeof(hexbuf);

	memset(hexbuf,0,sizeof(hexbuf));
	HexEncode(buf,len,hexbuf,&dlen);
	printf("%s:%s\r\n",tag,hexbuf);
//	_log_debug("%s:%s",tag,hexbuf);
}



int aes_encrypt(char* in, char* key, char* out)//, int olen)可能会设置buf长度
{
    if(!in || !key || !out) return 0;
    unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量
    int i;
    for(i=0; i<AES_BLOCK_SIZE; ++i)//iv一般设置为全0,可以设置其他，但是加密解密要一样就行
    	iv[i]=0;
    AES_KEY aes;
    if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    int len=strlen(in);//这里的长度是char*in的长度，但是如果in中间包含'\0'字符的话
    //那么就只会加密前面'\0'前面的一段，所以，这个len可以作为参数传进来，记录in的长度
    //至于解密也是一个道理，光以'\0'来判断字符串长度，确有不妥，后面都是一个道理。
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
    return 1;
}



int aes_decrypt(char* in, char* key, char* out)
{
    if(!in || !key || !out) return 0;
    unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量

    int i;
    for(i=0; i<AES_BLOCK_SIZE; ++i)//iv一般设置为全0,可以设置其他，但是加密解密要一样就行
    	iv[i]=0;

    AES_KEY aes;
    if(AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0){
        return 0;
    }
    int len=strlen(in);
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
    return 1;
}


void ShowCerts(SSL * ssl){
	X509 *cert;
	char *line;

	cert = SSL_get_peer_certificate(ssl);
	if (cert != NULL) {
		printf("数字证书信息:\n");
		line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
		printf("证书: %s\n", line);
		free(line);
		line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
		printf("颁发者: %s\n", line);
		free(line);
		X509_free(cert);
	} else
		printf("无证书信息！\n");
}

int main(int argc, char **argv){
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	SSL_CTX *ctx;
	SSL *ssl;

	if (argc != 3) {
		printf("参数格式错误！正确用法如下：\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",argv[0], argv[0]);
		exit(0);
	}


	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
	ctx = SSL_CTX_new(SSLv23_client_method());
	if (ctx == NULL) {
		ERR_print_errors_fp(stdout);
		exit(1);
	}


	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Socket");
		exit(errno);
	}
	printf("socket created\n");


	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
		perror(argv[1]);
		exit(errno);
	}
	printf("address created\n");

	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
		perror("Connect ");
		exit(errno);
	}
	printf("server connected\n");


	ssl = SSL_new(ctx);
	SSL_set_fd(ssl, sockfd);

	if (SSL_connect(ssl) == -1)
		ERR_print_errors_fp(stderr);
	else {
		printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
		ShowCerts(ssl);
	}

	char testout[2048];
	bzero(testout,2048);
	char hexbuf[64];
	int dlen = sizeof(hexbuf);
	memset(hexbuf, 0, sizeof(hexbuf));
	HexDecode(key, 32, hexbuf, &dlen);

	aes_encrypt(testin,hexbuf,testout);
	printf("aes_encrypt =%s\n",testout);
//	printHex("main",testout,2048);

	bzero(buffer, MAXBUF + 1);
//	strcpy(buffer, "from client->server");
	strcpy(buffer,"{\"ver\": 0,\"idx\": 3,\"cmd\":0,\"gtw\":\"AABBCC\",\"date\": 1473504748}\r\n");
	len = SSL_write(ssl, buffer, strlen(buffer));
	if (len < 0)
		printf("消息'%s'发送失败！错误代码是%d，错误信息是'%s'\n",buffer, errno, strerror(errno));
	else
		printf("消息'%s'发送成功，共发送了%d个字节！\n",buffer, len);


	bzero(buffer, MAXBUF + 1);

	len = SSL_read(ssl, buffer, MAXBUF);
	if (len > 0)
		printf("接收消息成功:'%s'，共%d个字节的数据\n",buffer, len);
	else {
		printf("消息接收失败！错误代码是%d，错误信息是'%s'\n",errno, strerror(errno));
		goto finish;
	}





finish:
	SSL_shutdown(ssl);
	SSL_free(ssl);
	close(sockfd);
	SSL_CTX_free(ctx);
	return 0;
}
